/**
 * com.sekati.crypt.TEA
 * @version 1.0.1
 * @author jason m horwitz | sekati.com
 * Copyright (C) 2007  jason m horwitz, Sekat LLC. All Rights Reserved.
 * Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
 * 
 * Sourced from ascrypt for dependencies only - version 2.0, author Mika Pamu
 * Original Javascript implementation:
 * Chris Veness, Movable Type Ltd: www.movable-type.co.uk
 * Algorithm: David Wheeler & Roger Needham, Cambridge University Computer Lab
 * @see http://www.movable-type.co.uk/scripts/TEAblock.html
 */

import com.sekati.crypt.ICipher;
/**
 * Encrypts and decrypts text with the TEA (Block) algorithm.
 */
class com.sekati.crypt.TEA implements ICipher {
	/**
	 * Encrypts a string with the specified key.
	 * @param src (String) string to encrypt
	 * @param key (String) encryption key
	 * @return String 
	 */
	public static function encrypt(src:String, key:String):String {
		var v:Array = charsToLongs( strToChars( src ) );
		var k:Array = charsToLongs( strToChars( key ) );
		var n:Number = v.length;
		if (n == 0) return "";
		if (n == 1) v[n++] = 0;
		var z:Number = v[n - 1], y:Number = v[0], delta:Number = 0x9E3779B9;
		var mx:Number, e:Number, q:Number = Math.floor( 6 + 52 / n ), sum:Number = 0;
		while (q-- > 0) {
			sum += delta;
			e = sum >>> 2 & 3;
			for (var p:Number = 0; p < n - 1 ; p++) {
				y = v[p + 1];
				mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
				z = v[p] += mx;
			}
			y = v[0];
			mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
			z = v[n - 1] += mx;
		}
		return charsToHex( longsToChars( v ) );
	}
	/**
	 * Decrypts a string with the specified key.
	 * @param src (String) string to decrypt
	 * @param key (String) decryption key
	 * @return String 
	 */
	public static function decrypt(src:String, key:String):String {
		var v:Array = charsToLongs( hexToChars( src ) );
		var k:Array = charsToLongs( strToChars( key ) );
		var n:Number = v.length;
		if (n == 0) return "";
		var z:Number = v[n - 1], y:Number = v[0], delta:Number = 0x9E3779B9;
		var mx:Number, e:Number, q:Number = Math.floor( 6 + 52 / n ), sum:Number = q * delta;
		while (sum != 0) {
			e = sum >>> 2 & 3;
			for(var p:Number = n - 1; p > 0 ; p--) {
				z = v[p - 1];
				mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
				y = v[p] -= mx;
			}
			z = v[n - 1];
			mx = (z >>> 5 ^ y << 2) + (y >>> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z);
			y = v[0] -= mx;
			sum -= delta;
		}
		return charsToStr( longsToChars( v ) );
	}
	private static function charsToLongs(chars:Array):Array {
		var temp:Array = new Array( Math.ceil( chars.length / 4 ) );
		for (var i:Number = 0; i < temp.length ; i++) {
			temp[i] = chars[i * 4] + (chars[i * 4 + 1] << 8) + (chars[i * 4 + 2] << 16) + (chars[i * 4 + 3] << 24);
		}
		return temp;
	}
	private static function longsToChars(longs:Array):Array {
		var codes:Array = new Array( );
		for (var i:Number = 0; i < longs.length ; i++) {
			codes.push( longs[i] & 0xFF, longs[i] >>> 8 & 0xFF, longs[i] >>> 16 & 0xFF, longs[i] >>> 24 & 0xFF );
		}
		return codes;
	}
	private static function charsToHex(chars:Array):String {
		var result:String = new String( "" );
		var hexes:Array = new Array( "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" );
		for (var i:Number = 0; i < chars.length ; i++) {
			result += hexes[chars[i] >> 4] + hexes[chars[i] & 0xf];
		}
		return result;
	}
	private static function hexToChars(hex:String):Array {
		var codes:Array = new Array( );
		for (var i:Number = (hex.substr( 0, 2 ) == "0x") ? 2 : 0; i < hex.length ; i += 2) {
			codes.push( parseInt( hex.substr( i, 2 ), 16 ) );
		}
		return codes;
	}
	private static function charsToStr(chars:Array):String {
		var result:String = new String( "" );
		for (var i:Number = 0; i < chars.length ; i++) {
			result += String.fromCharCode( chars[i] );
		}
		return result;
	}
	private static function strToChars(str:String):Array {
		var codes:Array = new Array( );
		for (var i:Number = 0; i < str.length ; i++) {
			codes.push( str.charCodeAt( i ) );
		}
		return codes;
	}
	private function TEA() {
	}
}